/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "resize_load_sit.h"

#include <locale.h>
#include <stdbool.h>
#include <time.h>

#include "node.h"
#include "device_manager.h"
#include "hmfs_common.h"

namespace OHOS {
namespace Hmfs {
ResizeLoadSit::ResizeLoadSit(ResizeLoad *loadPtr) : load_(loadPtr)
{
	config_ = load_->GetConfig();
	sbInfo_ = load_->GetSbInfo();
}

bool ResizeLoadSit::IsSyncDataRecord()
{
    return !IsCheckpointFlags(sbInfo_->ckptData, CP_UMOUNT_FLAG) ||
        config_->zonedModel == HMFS_ZONED_HM;
}

int32_t ResizeLoadSit::BuildSitInfo()
{
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct CheckPointData *cpData = sbInfo_->ckptData;

    struct SitInfo *sitInfo = static_cast<struct SitInfo *>(malloc(sizeof(struct SitInfo)));
    if (sitInfo == nullptr) {
        HMFS_ERROR("\tError: Malloc failed for build_sit_info!");
        return -ENOMEM;
    }

    sbInfo_->smInfoTable->sitInfo = sitInfo;
    unsigned int mainSegments = sbInfo_->smInfoTable->mainSegments;
    sitInfo->segEntries = static_cast<struct SegEntry *>(calloc(mainSegments * sizeof(struct SegEntry), 1));
    if (sitInfo->segEntries == nullptr) {
        HMFS_ERROR("\tError: Calloc failed for build_sit_info!");
        free(sitInfo);
        return -ENOMEM;
    }

    unsigned int bitmapSize = mainSegments * SIT_VBLOCK_MAP_SIZE;
    if (IsSyncDataRecord()) {
        bitmapSize += bitmapSize;
    }
    sitInfo->bitmap = static_cast<unsigned char *>(calloc(bitmapSize, 1));
    if (sitInfo->bitmap == nullptr) {
        HMFS_ERROR("\tError: Calloc failed for build_sit_info!!");
        free(sitInfo->segEntries);
        free(sitInfo);
        return -ENOMEM;
    }

    unsigned char *bitmap = sitInfo->bitmap;
    for (unsigned int start = 0; start < mainSegments; start++) {
        sitInfo->segEntries[start].curValidBitmap = bitmap;
        bitmap += SIT_VBLOCK_MAP_SIZE;
        if (IsSyncDataRecord()) {
            sitInfo->segEntries[start].ckptValidBitmap = bitmap;
            bitmap += SIT_VBLOCK_MAP_SIZE;
        }
    }

    unsigned int sitSegments = GetLeValue(sbData->segmentCountInSIT) >> 1;
    bitmapSize = LE32_TO_NATIVE(cpData->sitVersionBitmapSize);
    char *srcBitmap = static_cast<char *>(load_->GetBitmapPtr(SIT_BITMAP));
    char *dstBitmap = static_cast<char *>(malloc(bitmapSize));
    if (dstBitmap == nullptr) {
        HMFS_ERROR("\tError: Malloc failed for build_sit_info!!");
        free(sitInfo->bitmap);
        free(sitInfo->segEntries);
        free(sitInfo);
        return -ENOMEM;
    }

    memcpy(dstBitmap, srcBitmap, bitmapSize);

    sitInfo->sitBaseAddr = GetLeValue(sbData->sitBlkId);
    sitInfo->sitBlocks = sitSegments << sbInfo_->logBlksPerSeg;
    sitInfo->writtenValidBlocks = GetLeValue(cpData->validBlockCount);
    sitInfo->sitBitmap = dstBitmap;
    sitInfo->bitmapSize = bitmapSize;
    sitInfo->dirtySentries = 0;
    sitInfo->sentsPerBlock = SIT_ENTRIES_PER_BLOCK;
    sitInfo->elapsedTime = GetLeValue(cpData->elapsedTime);
    return SUCCESS_CODE;
}

uint32_t ResizeLoadSit::GetStartSumBlock()
{
    uint32_t cpPackStartSum = sbInfo_->ckptData->cpPackStartSum;
    return load_->GetStartCpAddr() + LE32_TO_NATIVE(cpPackStartSum);
}

uint32_t ResizeLoadSit::GetSumBlkAddr(int base, int type)
{
    uint32_t cpPackTotalBlockCount = sbInfo_->ckptData->cpPackBlockCount;
    return load_->GetStartCpAddr() + LE32_TO_NATIVE(cpPackTotalBlockCount) - (base + 1) + type;
}

void ResizeLoadSit::ReadCompactSummaries()
{
    uint32_t start = GetStartSumBlock();
    char *kaddr = static_cast<char *>(malloc(HMFS_BLKSIZE));
    ASSERT(kaddr);

    int32_t ret = HmfsIo::GetInstance().DevReadBlock(kaddr, start++);
    ASSERT(ret >= 0);
    struct CurSegmentInfo *curSeg = (struct CurSegmentInfo *)(sbInfo_->smInfoTable->curSegmentArray + CURSEG_HOT_DATA);
    memcpy(&curSeg->segSumBlk->journal.nNats, kaddr, SUM_JOURNAL_SIZE);

    curSeg = (struct CurSegmentInfo *)(sbInfo_->smInfoTable->curSegmentArray + CURSEG_COLD_DATA);
    memcpy(&curSeg->segSumBlk->journal.nSits, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE);

    unsigned int offset = 2 * SUM_JOURNAL_SIZE;
    for (uint32_t i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
        unsigned short blk_off;
        struct CurSegmentInfo *cursegInfo = static_cast<struct CurSegmentInfo *>(sbInfo_->smInfoTable->curSegmentArray + i);
        ResetCurSegment(sbInfo_, i);

        if (cursegInfo->allocType == SSR) {
            blk_off = sbInfo_->blocksPerSeg;
        } else {
            blk_off = cursegInfo->nextBlkOffset;
        }
        ASSERT(blk_off <= ENTRIES_IN_SUM);

        for (uint32_t j = 0; j < blk_off; j++) {
            struct SummaryEntry *s;
            s = (struct SummaryEntry *)(kaddr + offset);
            cursegInfo->segSumBlk->entries[j] = *s;
            offset += SUMMARY_SIZE;
            if (offset + SUMMARY_SIZE <=
                    HMFS_BLKSIZE - SUM_FOOTER_SIZE)
                continue;
            memset(kaddr, 0, HMFS_BLKSIZE);
            ret = HmfsIo::GetInstance().DevReadBlock(kaddr, start++);
            ASSERT(ret >= 0);
            offset = 0;
        }
    }
    free(kaddr);
}

void ResizeLoadSit::RestoreNodeSummary(unsigned int segno, struct SummaryBlockData *sumblk)
{
    if (sumblk == nullptr) {
        HMFS_ERROR("sumblk is nullptr");
        return;
    }
    struct NodeData *nodeBlk = static_cast<struct NodeData *>(malloc(HMFS_BLKSIZE));
    ASSERT(nodeBlk);

    /* scan the node segment */
    uint32_t addr = sbInfo_->smInfoTable->mainBlkaddr + ((segno) << sbInfo_->logBlksPerSeg);
    struct SummaryEntry *sumEntry = &sumblk->entries[0];
    for (uint32_t i = 0; i < sbInfo_->blocksPerSeg; i++, sumEntry++) {
        int32_t ret = HmfsIo::GetInstance().DevReadBlock(nodeBlk, addr);
        ASSERT(ret >= 0);
        sumEntry->nid = nodeBlk->footer.nid;
        addr++;
    }
    free(nodeBlk);
}

void ResizeLoadSit::ReadNormalSummaries(int type)
{
    struct CheckPointData *cpData = sbInfo_->ckptData;
    unsigned int segno = 0;
    uint32_t blkAddr = 0;
    int ret;

    if (IS_DATASEG(type)) {
        segno = GetLeValue(cpData->curDataSegNo[type]);
        if (IsCheckpointFlags(cpData, CP_UMOUNT_FLAG)) {
            blkAddr = GetSumBlkAddr(NR_CURSEG_TYPE, type);
        } else {
            blkAddr = GetSumBlkAddr(NR_CURSEG_DATA_TYPE, type);
        }
    } else {
        segno = GetLeValue(cpData->curNodeSegNo[type - CURSEG_HOT_NODE]);
        if (IsCheckpointFlags(cpData, CP_UMOUNT_FLAG)) {
            blkAddr = GetSumBlkAddr( NR_CURSEG_NODE_TYPE, type - CURSEG_HOT_NODE);
        } else {
            blkAddr = sbInfo_->smInfoTable->ssaBlkaddr + segno;
        }
    }

    struct SummaryBlockData *sumBlk = static_cast<struct SummaryBlockData *>(malloc(sizeof(*sumBlk)));
    ASSERT(sumBlk);

    ret = HmfsIo::GetInstance().DevReadBlock(sumBlk, blkAddr);
    ASSERT(ret >= 0);

    if (IS_NODESEG(type) && !IsCheckpointFlags(cpData, CP_UMOUNT_FLAG)) {
        RestoreNodeSummary(segno, sumBlk);
    }
    struct CurSegmentInfo *curSeg = CURSEG_I(sbInfo_, type);
    memcpy(curSeg->segSumBlk, sumBlk, sizeof(*sumBlk));
    ResetCurSegment(sbInfo_, type);
    free(sumBlk);
}

void ResizeLoadSit::UpdateCurSegSummaries()
{
    int type = CURSEG_HOT_DATA;

    if (IsCheckpointFlags(sbInfo_->ckptData, CP_COMPACT_SUM_FLAG)) {
        ReadCompactSummaries();
        type = CURSEG_HOT_NODE;
    }

    for (; type <= CURSEG_COLD_NODE; type++) {
        ReadNormalSummaries(type);
    }
}

int32_t ResizeLoadSit::BuildCurSegment()
{
    struct CheckPointData *cpData = sbInfo_->ckptData;
    unsigned short blkOffset;
    unsigned int segNum;
    int i;

    struct CurSegmentInfo *array = static_cast<struct CurSegmentInfo *>(malloc(sizeof(*array) * NR_CURSEG_TYPE));
    if (!array) {
        HMFS_ERROR("\tError: Malloc failed for build_curseg!");
        return -ENOMEM;
    }
    sbInfo_->smInfoTable->curSegmentArray = array;
    
    for (i = 0; i < NR_CURSEG_TYPE; i++) {
        array[i].segSumBlk = static_cast<struct SummaryBlockData *>(calloc(sizeof(*(array[i].segSumBlk)), 1));
        if (!array[i].segSumBlk) {
            HMFS_ERROR("\tError: Calloc failed for build_curseg!!");
            for(--i ; i >= 0; --i) {
                free(array[i].segSumBlk);
            }
            free(array);
            return -ENOMEM;
        }

        if (i <= CURSEG_COLD_DATA) {
            blkOffset = GetLeValue(cpData->curDataBlkOffset[i]);
            segNum = GetLeValue(cpData->curDataSegNo[i]);
        }
        if (i > CURSEG_COLD_DATA) {
            blkOffset = GetLeValue(cpData->curNodeBlkOffset[i - CURSEG_HOT_NODE]);
            segNum = GetLeValue(cpData->curNodeSegNo[i - CURSEG_HOT_NODE]);
        }
        ASSERT(segNum < sbInfo_->smInfoTable->mainSegments);
        ASSERT(blkOffset < DEFAULT_BLOCKS_PER_SEGMENT);

        array[i].segNum = segNum;
        array[i].zone = (segNum / sbInfo_->segmentsPerSec) / sbInfo_->sectionsPerZone;
        array[i].nextSegNum = NULL_SEGNO;
        array[i].nextBlkOffset = blkOffset;
        array[i].allocType = cpData->allocType[i];
    }
    UpdateCurSegSummaries();
    return SUCCESS_CODE;
}

int32_t ResizeLoadSit::BuildSegmentBegin()
{
    HMFS_DEBUG("Enter BuildSegmentBegin");
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct CheckPointData *cpData = sbInfo_->ckptData;
    struct SegmentInfoTable *smInfoTable =
        static_cast<struct SegmentInfoTable *>(malloc(sizeof(struct SegmentInfoTable)));
    if (!smInfoTable) {
        HMFS_ERROR("\tError: Malloc failed for build_segment_manager!");
        return -ENOMEM;
    }

    /* init sm info table */
    sbInfo_->smInfoTable = smInfoTable;
    smInfoTable->seg0Blkaddr = GetLeValue(sbData->segment0BlkId);
    smInfoTable->mainBlkaddr = GetLeValue(sbData->mainBlkId);
    smInfoTable->segmentCount = GetLeValue(sbData->segmentCount);
    smInfoTable->reservedSegments = GetLeValue(cpData->rsvdSegmentCount);
    smInfoTable->ovpSegments = GetLeValue(cpData->overprovSegmentCount);
    smInfoTable->mainSegments = GetLeValue(sbData->segmentCountInMain);
    smInfoTable->ssaBlkaddr = GetLeValue(sbData->ssaBlkId);

    if (BuildSitInfo() != SUCCESS_CODE || BuildCurSegment() != SUCCESS_CODE) {
        free(smInfoTable);
        return -ENOMEM;
    }
    return 0;
}

int32_t ResizeLoadSit::BuildSitEntries()
{
    struct SitInfo *sitInfo = SIT_I(sbInfo_);
    struct CurSegmentInfo *curseg = CURSEG_I(sbInfo_, CURSEG_COLD_DATA);
    struct JournalEntry *journal = &curseg->segSumBlk->journal;
    struct SegEntry *se;
    struct sitEntry sit;
    int sitBlkCnt = SIT_BLK_CNT(sbInfo_);
    unsigned int segno;
    unsigned int startBlk = 0;
    struct sitBlockData *sitBlk = static_cast<struct sitBlockData *>(calloc(BLOCK_SZ, 1));
    if (!sitBlk) {
        HMFS_ERROR("\tError: Calloc failed for build_sit_entries!");
        return -ENOMEM;
    }

    do {
        unsigned int readed = load_->HmfsRaMetaPages(startBlk, MAX_RA_BLOCKS, META_SIT);
        segno = startBlk * sitInfo->sentsPerBlock;
        unsigned int end = (startBlk + readed) * sitInfo->sentsPerBlock;

        for (; segno < end && segno < MAIN_SEGS(sbInfo_); segno++) {
            se = &sitInfo->segEntries[segno];
            GetCurrentSitPage(sbInfo_, segno, sitBlk);
            sit = sitBlk->entries[SIT_ENTRY_OFFSET(sitInfo, segno)];
            CheckBlockCount(segno, &sit);
            SegInfoFromRawSit(se, &sit);
        }
        startBlk += readed;
    } while (startBlk < sitBlkCnt);

    free(sitBlk);

    if (SitsInCursum(journal) > SIT_JOURNAL_ENTRIES) {
        HMFS_ERROR("\tError: build_sit_entries truncate nSits(%u) to SIT_JOURNAL_ENTRIES(%zu)",
            SitsInCursum(journal), SIT_JOURNAL_ENTRIES);
        journal->nSits = NATIVE_TO_LE16(SIT_JOURNAL_ENTRIES);
        config_->fixOn = 1;
    }

    for (uint32_t i = 0; i < SitsInCursum(journal); i++) {
        segno = LE32_TO_NATIVE(SegnoInJournal(journal, i));
        if (segno >= MAIN_SEGS(sbInfo_)) {
            HMFS_ERROR("\tError: build_sit_entries: segno(%u) is invalid!!!", segno);
            journal->nSits = NATIVE_TO_LE16(i);
            config_->fixOn = 1;
            continue;
        }

        se = &sitInfo->segEntries[segno];
        sit = SitInJournal(journal, i);

        CheckBlockCount(segno, &sit);
        SegInfoFromRawSit(se, &sit);
    }
    return SUCCESS_CODE;
}

void ResizeLoadSit::InsideSegInfoFromRawSit(struct SegEntry *se, struct sitEntry *rawSit)
{
    se->validBlocks = GET_SIT_VBLOCKS(rawSit);
    memcpy(se->curValidBitmap, rawSit->blockBitmap, SIT_VBLOCK_MAP_SIZE);
    se->type = GET_SIT_TYPE(rawSit);
    se->origType = GET_SIT_TYPE(rawSit);
    se->modTime = LE64_TO_NATIVE(rawSit->modTime);
}

void ResizeLoadSit::SegInfoFromRawSit(struct SegEntry *se, struct sitEntry *rawSit)
{
    InsideSegInfoFromRawSit(se, rawSit);
    if (!NeedFsyncDataRecord(sbInfo_, config_)) {
        return;
    }
    se->ckptValidBlocks = se->validBlocks;
    memcpy(se->ckptValidBitmap, se->curValidBitmap, SIT_VBLOCK_MAP_SIZE);
    se->ckptType = se->type;
}

void ResizeLoadSit::CheckBlockCount(unsigned int segno, struct sitEntry *rawSit)
{
    struct SegmentInfoTable *smInfo = SM_I(sbInfo_);
    unsigned int end_segno = smInfo->segmentCount - 1;
    int validBlocks = 0;

    /* check segment usage */
    if (GET_SIT_VBLOCKS(rawSit) > sbInfo_->blocksPerSeg)
        HMFS_ERROR("Invalid SIT vblocks: segno=0x%x, %u",
                segno, GET_SIT_VBLOCKS(rawSit));

    /* check boundary of a given segment number */
    if (segno > end_segno)
        HMFS_ERROR("Invalid SEGNO: 0x%x", segno);

    /* check bitmap with valid block count */
    for (uint32_t i = 0; i < SIT_VBLOCK_MAP_SIZE; i++) {
        validBlocks += HmfsCommon::GetInstance().GetBitsInByte(rawSit->blockBitmap[i]);
    }
    if (GET_SIT_VBLOCKS(rawSit) != validBlocks)
        HMFS_ERROR("Wrong SIT valid blocks: segno=0x%x, %u vs. %u",
                segno, GET_SIT_VBLOCKS(rawSit), validBlocks);

    if (GET_SIT_TYPE(rawSit) >= NO_CHECK_TYPE)
        HMFS_ERROR("Wrong SIT type: segno=0x%x, %u",
                segno, GET_SIT_TYPE(rawSit));
}

int32_t ResizeLoadSit::LateBuildSegmentManager()
{
    HMFS_DEBUG("Enter LateBuildSegmentManager");
    if (sbInfo_->segManagerDone) {
        return EXIT_ERR_CODE; /* this function was already called */
    }
    sbInfo_->segManagerDone = true;
    if (BuildSitEntries()) {
        free (sbInfo_->smInfoTable);
        return -ENOMEM;
    }
    return SUCCESS_CODE;
}

} // namespace Hmfs
} // namespace OHOS
